﻿using System;
using System.Linq;
using log4net;
using log4net.Layout;
using log4net.Appender;
using log4net.Repository.Hierarchy;
using log4net.Core;
using System.IO;

namespace VNext.Log4Net
{
    public enum LogLevel
    {
        Debug = 0,
        Information = 1,
        Warning = 2,
        Error = 3,
        Exception = 4,
        None = 5
    }

    public class Logger
    {
        private readonly string DefaultLogName = "General";

        private readonly string ConversionPattern = "%date{yyyy-MM-dd HH:mm:ss.ffff},%level,%logger,%message %newline";

        public LogLevel LogLevel { get; set; } = LogLevel.Debug;

        public Logger()
        {
            Init();
        }

        public void Init(string logFilename = "")
        {
            try
            {
                var patternLayout = new PatternLayout();
                patternLayout.ConversionPattern = ConversionPattern;
                patternLayout.ActivateOptions();

                RollingFileAppender roller = new RollingFileAppender();
                roller.AppendToFile = true;
                roller.Encoding = System.Text.Encoding.UTF8;
                if (string.IsNullOrEmpty(logFilename))
                    logFilename = $" {Directory.GetCurrentDirectory()}\\VNext.{Environment.MachineName}.log";
                roller.File = logFilename;
                roller.Layout = patternLayout;
                roller.RollingStyle = RollingFileAppender.RollingMode.Date;
                roller.StaticLogFileName = true;
                roller.DatePattern = ".yyyyMMdd";
                roller.PreserveLogFileNameExtension = true;
                roller.ActivateOptions();

                Hierarchy hierarchy = (Hierarchy)LogManager.GetRepository();
                hierarchy.Root.RemoveAllAppenders();
                hierarchy.Root.AddAppender(roller);
                hierarchy.Root.Level = Level.All;
                hierarchy.Configured = true;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        private bool HasAppenders(ILog log)
        {
            var logger = log.Logger as log4net.Repository.Hierarchy.Logger;
            return logger?.Appenders?.OfType<log4net.Appender.IAppender>().Any() ?? false;
        }

        private ILog GetLogger(string name)
        {

            var logger = LogManager.GetLogger(name);
            if (logger != null && HasAppenders(logger))
            {
                return logger;
            }

            // fallback if no such logger configured
            logger = LogManager.GetLogger(DefaultLogName);
            if (logger != null)
            {
                return logger;
            }

            return LogManager.GetCurrentLoggers().FirstOrDefault();
        }

        public void LogDebugInformation(object sender, string logMessage)
        {
            LogDebugInformation(sender.GetType().Name, logMessage);
        }

        public void LogDebugInformation(string className, string logMessage)
        {
            if (LogLevel == LogLevel.Debug)
            {
                logMessage = logMessage.TrimEnd('\r', '\n');

                ILog log = GetLogger(className);
                if (log.Logger.Name == DefaultLogName)
                {
                    // ??? log.Debug($"{className} - {logMessage}"); ???
                    log.Debug($"{className},{logMessage}");
                }
                else
                {
                    log.Debug($"{logMessage}");
                }
                LogManager.Flush(1);
            }
        }

        public void LogInformation(string logMessage)
        {
            LogInfo(DefaultLogName, logMessage);
        }

        public void LogWarning(string logMessage)
        {
            LogWarning(DefaultLogName, logMessage);
        }

        public void LogError(string logMessage)
        {
            LogError(DefaultLogName, logMessage);
        }


        public void LogException(string logMessage, Exception e)
        {
            LogException(DefaultLogName, logMessage, e);
        }


        public void LogDebug(string logger, string logMessage)
        {
            if (LogLevel <= LogLevel.Debug)
            {
                ILog log = GetLogger(logger);
                log?.Debug(logMessage.TrimEnd('\r', '\n'));
            }
        }

        public void LogInfo(string logger, string logMessage)
        {
            if (LogLevel <= LogLevel.Information)
            {
                ILog log = GetLogger(logger);
                log?.Info(logMessage.TrimEnd('\r', '\n'));
            }
        }

        public void LogWarning(string logger, string logMessage)
        {
            if (LogLevel <= LogLevel.Warning)
            {
                ILog log = GetLogger(logger);
                log?.Warn(logMessage.TrimEnd('\r', '\n'));
            }
        }

        public void LogError(string logger, string logMessage)
        {
            if (LogLevel <= LogLevel.Error)
            {
                ILog log = GetLogger(logger);
                log?.Error(logMessage.TrimEnd('\r', '\n'));
            }
        }

        public void LogException(string logger, string logMessage, Exception logException)
        {
            if (LogLevel <= LogLevel.Exception)
            {
                ILog log = GetLogger(logger);
                log?.Error($"{logMessage.TrimEnd('\r', '\n')} - {logException.Message}.\r\nType:{logException.GetType().Name}\r\nStack Trace: {logException.StackTrace}");

                if (logException.InnerException != null)
                    LogException(logger, "Inner exception", logException.InnerException);

                if (logException is AggregateException aggregateException)
                {
                    int nInner = 1;
                    foreach (var inner in aggregateException.InnerExceptions)
                    {
                        LogException(logger, $"{aggregateException.GetType().Name}'s Inner exception {nInner++}", inner);
                    }
                }
            }
        }
    }
}
